#ifndef PRYN_TYPE_H
#define PRYN_TYPE_H

typedef struct PrynType PrynType; ///< A type of an object.

#include <pryn/platform.h>

typedef PrynDList (PrynType) PrynTypeList; ///< A linked list of types.

#include <pryn/library.h>
#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/string.h>
#include <pryn/tag.h>
#include <pryn/utility.h>
#include <pryn/value.h>

/** @defgroup PrynType PrynType C API
@{ */

PrynCallbackTypedef (PrynResult, PrynTypeCreateFunction, (PrynValue **value, PrynType *type, void *initial, void *data))
	/**< @brief Create an instance of this type.

	@param[out] value Write the output value here. If the function fails, either don't write anything or write 0.
	@param type The type this function is registered in.
	@param initial An optional initial value.
	@param data Data field assigned to this function.
	@returns #PrynResultSuccess or error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynTypeDestroyFunction, (PrynValue *value, void *data)) ///< Destroy an instance of this type. The implementor must is responsible for freeing the value's pointer.

PrynCallbackTypedef (PrynResult, PrynTypeConnectionImageFunction, (PrynValue *value, int width, int height, PrynColor *image, PrynColor *fallback, void *data))
	/**< @brief Function called to generate an image to be used as the background of a connection carrying a value of this type.
	
	Increasing width is closer to the input pin; increasing height is perpendicular to the connection line. image and/or fallback should not be written if they are 0.

	@param value A value whose type this function is registered in.
	@param width The width in pixels of @e image.
	@param height The height in pixels of @e image.
	@param[out] image A color image to be written to if non-zero. The image is indexed in [x + y * @e width] order.
	@param[out] fallback A fallback color to be written to if non-zero. This is used if the editor can't support image-based connection lines.
	@param data Data field assigned to this function.
	@returns #PrynResultSuccess or error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynTypeColorsFunction, (PrynValue *value, PrynColor *outer, PrynColor *inner, void *data))
	/**< @brief Retrieve the inner and outer colors of the value for a pin rendered with this value.

	@param value The value with the type this function is registered in.
	@param[out] outer The outside color of the pin.
	@param[out] inner The inside color of the pin.
	@param data Data field assigned to this function.
	@returns #PrynResultSuccess or error code; see #PrynResult. */

PrynImport (PrynResult, prynTypeCreate, (PrynType **type, PrynLibrary *library, const PrynString *id)) 
	/**< Create a new type, or retrieve an already-created type if the id matches.

	@param[out] type Receives the type or zero on failure.
	@param library The library this type is derived from.
	@param id The identifier to match. This identifier should be globally unique.
	@returns #PrynResultSuccess or error code; see #PrynResult. */

PrynImport (PrynResult, prynTypeFind, (PrynType **type, PrynState *state, const PrynString *id)) ///< Find a type, creating an unlinked type if it doesn't yet exist.

PrynImport (PrynResult, prynTypeDestroy, (PrynType *type)) ///< Completely destroy the type, freeing it from the state. This does not affect any values using the type as they're not tracked. As a result, this should only be called before the type is in use.

PrynImport (PrynResult, prynTypeState, (PrynType *type, PrynState **result)) ///< Retrieve the state the type is created within.
PrynImport (PrynResult, prynTypeTags, (PrynType *type, PrynTagList **result)) ///< Retreive the list of tags associated with the type.
PrynImport (PrynResult, prynTypeId, (PrynType *type, PrynString *result)) ///< Retrieve the identifier of the type.
PrynImport (PrynResult, prynTypeName, (PrynType *type, PrynString *result)) ///< Retrieve the name of the type.
PrynImport (PrynResult, prynTypeDescription, (PrynType *type, PrynString *result)) ///< Retrieve the description of the type.

PrynImport (PrynResult, prynTypeSetName, (PrynType *type, const PrynString *value)) ///< Set the name of the type. The value is not copied.
PrynImport (PrynResult, prynTypeSetDescription, (PrynType *type, const PrynString *value)) ///< Set the description of the type. The value is not copied.
#define prynTypeSetNameLiteral(TYPE, VALUE) do { PrynString LITERAL; prynStringLiteral (&LITERAL, (VALUE)); prynTypeSetName ((TYPE), &LITERAL); } while (0) /**< @brief Set the name of the type to a literal string. */
#define prynTypeSetDescriptionLiteral(TYPE, VALUE) do { PrynString LITERAL; prynStringLiteral (&LITERAL, (VALUE)); prynTypeSetDescription ((TYPE), &LITERAL); } while (0) /**< @brief Set the description of the type to a literal string.  */

PrynImportSetCallback (, Type, CreateFunction) ///< Set the creation function. This can be 0.
PrynImportSetCallback (, Type, DestroyFunction) ///< Set the destruction function. This can be 0, in which case the value's pointer will be freed.
PrynImportSetCallback (, Type, ConnectionImageFunction) ///< Set the value connection image function. If value is zero, it's reset to a default of passing off to the general connection image function.
PrynImportSetCallback (, Type, ColorsFunction) ///< Set the value colors function. This is used to get inner and outer colors for a pin carrying this value.

PrynImport (PrynResult, prynTypeClearConnectionImage, (int width, int height, PrynColor *image, PrynColor *fallback, PrynColor value)) ///< Utility function for #PrynTypeConnectionImageFunction implementations that sets the fallback and image, if provided, to a single color.

#ifdef PrynInternalStructs
/** @brief Type of a pin. */
struct PrynType
{
#ifdef PrynInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.
	PrynCommonResource pCommonResource; ///< Common fields you'd expect in a resource, like name and description.
	PrynString pId; ///< Unique identifier of the type.

	PrynType *pNext; ///< Next type in the list or 0 if this is last.
	PrynType *pPrevious; ///< Previous type in the list or 0 if this is first.

	PrynState *pState; ///< State this type is within.
	PrynLibrary *pLibrary; ///< The library this type is defined within or 0 if it's currently unlinked.
	PrynMediator *pMediator; ///< Mediator this type is from or 0 if it's not mediated.
	PrynValueList pValues; ///< List of values of this type.
	
	PrynType **pCasts; ///< The other types this can be casted to, an array of length castCount.
	uintptr_t pCastCount; ///< The number of elements in casts.
	
	PrynCallback (PrynTypeCreateFunction, pCreateFunction); ///< Create an instance of this type. This is zero if the type isn't loaded.
	PrynCallback (PrynTypeDestroyFunction, pDestroyFunction); ///< Destroy an instance of this type. The default frees the pointer.
	PrynCallback (PrynTypeConnectionImageFunction, pConnectionImageFunction); ///< Value connection image function.
	PrynCallback (PrynTypeColorsFunction, pColorsFunction); ///< Inner and outer colors for a pin carrying this value.

/** @} */
#endif /* PrynInternal */

#if __cplusplus
	static inline PrynType *Create (PrynLibrary *library, const PrynString &id); ///< Create a new type, or retrieve an already-created type if the id matches.

	static inline PrynType *Find (PrynState *state, const PrynString &id); ///< Find a type, creating an unlinked type if it doesn't yet exist.

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

/** @name Properties
@{ */

	inline PrynState *state () { PrynState *result; prynCheckErrorNull (prynTypeState (this, &result)); return result; } ///< State the type is created within.
	inline PrynTagList *tags () { PrynTagList *result; checkError (prynTypeTags (this, &result)); return result; } ///< List of tags associated with the type.
	inline PrynString id () { PrynString result; checkError (prynTypeId (this, &result)); return result; } ///< The identifier of the type.
	inline PrynString name () { PrynString result; checkError (prynTypeName (this, &result)); return result; } ///< The name of the type.
	inline void name (const PrynString &value) { checkError (prynTypeSetName (this, &value)); } ///< Set the name of the type. The value is adopted, not copied.
	inline PrynString description () { PrynString result; checkError (prynTypeDescription (this, &result)); return result; } ///< The description of the type.
	inline void description (const PrynString &value) { checkError (prynTypeSetDescription (this, &value)); } ///< Set the description of the type. The value is adopted, not copied.

/** @} */

/** @name Callbacks
@{ */

	PrynCallbackWrapperDeclaration (Type, CreateFunction, createFunction, this->state (), (PrynValue **value, PrynType *type, void *initial, void *data), (value, type, initial, data)) ///< Set the creation function. This can be 0, which uninitialises the type.

	PrynCallbackWrapperDeclaration (Type, DestroyFunction, destroyFunction, this->state (), (PrynValue *value, void *data), (value, data)) ///< Set the destroy function. This can be 0, which uses a fallback that frees the pointer field of the value.

	PrynCallbackWrapperDeclaration (Type, ConnectionImageFunction, connectionImageFunction, this->state (), (PrynValue *value, int width, int height, PrynColor *image, PrynColor *fallback, void *data), (value, width, height, image, fallback, data)) ///< Set the value connection image function. If value is 0, it's reset to a default of passing off to the general connection image function.

	PrynCallbackWrapperDeclaration (Type, ColorsFunction, colorsFunction, this->state (), (PrynValue *value, PrynColor *outer, PrynColor *inner, void *data), (value, outer, inner, data)) ///< Set the value colors function. This is used to get inner and outer colors for a pin carrying the value.

/** @} */

	inline static void ClearConnectionImage (int width, int height, PrynColor *image, PrynColor *fallback, PrynColor value) { prynCheckErrorNull (prynTypeClearConnectionImage (width, height, image, fallback, value)); } ///< Utility function for #PrynTypeConnectionImageFunction implementations that sets the fallback and image, if provided, to a single color.
#endif /* __cplusplus */
};
#endif /* PrynInternalStructs */

#if __cplusplus
/** @brief A virtual class wrapper for a type. You can subclass this and overload member functions, as opposed to creating a normal type and assigning callbacks. */
class PrynTypeClass
{
protected:
	PrynType *pBase; ///< Link to the actual type object.

public:
	virtual PrynString id () const = 0; ///< The globally-unique identifier of the type.
	virtual PrynString defaultName () = 0; ///< Return the default name of the type, which can be overwritten using the #name property.
	virtual PrynString defaultDescription () = 0; ///< Return the default description of the type, which can be overwritten using the #description property.

	/// Merely assigns the base type to 0. Actual construction goes on in #construct.
	inline PrynTypeClass () : pBase (0) { }

	/// Assign the base type.
	virtual inline void construct (PrynLibrary *library);

	/** Allocate an instance of this type. This is for implementation of types.

	@param length The number of bytes to allocate into the pointer field of the result.
	@param initial If non-zero, this is used as the initial value of the pointer array; otherwise it's all zeroes.
	@returns A value with this type as the type, and a pointer composed of an array that's either a duplicate of @e initial or all zeroes. */
	inline PrynValue *allocateValue (size_t length, void *initial = 0);

	/** @name Properties
	@{ */

	inline PrynType *type () { return pBase; } ///< The internal type of this type.
	inline PrynState *state () { return pBase->state (); } ///< The state this type exists in.
	inline PrynTagList *tags () { return pBase->tags (); } ///< List of tags associated with the type.

	inline PrynString name () { return pBase->name (); } ///< The name of the type. The default is set to #defaultName.
	inline void name (const PrynString &value) { pBase->name (value); } ///< Assign the name of the type.
	inline PrynString description () { return pBase->description (); } ///< A description of the type. The default is set to #defaultDescription.
	inline void description (const PrynString &value) { pBase->description (value); } ///< Assign the description of the type.

	/** @} */ // (Properties section)

	inline static void ClearConnectionImage (int width, int height, PrynColor *image, PrynColor &fallback, PrynColor value)
	{
		PrynType::ClearConnectionImage (width, height, image, &fallback, value);
	}

protected:
	/// Create an instance of this type.
	virtual inline PrynValue *createFunction (void *initial);

	/// Free an instance of this type. The default does nothing.
	virtual inline void destroyFunction (PrynValue *value)
	{
	}

	/// Retrieve the colors to use on the outside and inside of a pin carrying this value.
	virtual inline void colorsFunction (PrynValue *value, PrynColor &outer, PrynColor &inner)
	{
	}

	/// Retrieve the image to use on a connection carrying this value.
	virtual inline void connectionImageFunction (PrynValue *value, int width, int height, PrynColor *image, PrynColor &fallback)
	{
		ClearConnectionImage (width, height, image, fallback, prynColorBlack);
	}

protected:
	static inline void createFunctionStatic (PrynValue **value, PrynType *type, void *initial, void *data);

	static inline void destroyFunctionStatic (PrynValue *value, void *data)
	{
		PrynTypeClass *self = (PrynTypeClass *) data;
		self->destroyFunction (value);
	}

	static inline void colorsFunctionStatic (PrynValue *value, PrynColor *outer, PrynColor *inner, void *data)
	{
		PrynTypeClass *self = (PrynTypeClass *) data;
		PrynColor outerValue = outer ? *outer : 0, innerValue = inner ? *inner : 0;
		self->colorsFunction (value, outerValue, innerValue);
		if (outer) *outer = outerValue;
		if (inner) *inner = innerValue;
	}

	static inline void connectionImageFunctionStatic (PrynValue *value, int width, int height, PrynColor *image, PrynColor *fallback, void *data)
	{
		PrynTypeClass *self = (PrynTypeClass *) data;
		PrynColor fallbackValue = fallback ? *fallback : 0;
		self->connectionImageFunction (value, width, height, image, fallbackValue);
		if (fallback) *fallback = fallbackValue;
	}
};
#endif /* __cplusplus */

/** @} */ // (group)

#endif /* PRYN_TYPE_H */
